package ca.tatham.scheduler;

public class TaskQueue<E extends Task> extends PrioritizedQueue<E> {
	public TaskQueue() {
		super();
	}

	public E popComplete() {
		E task = peek();
		while (task != null && task.isComplete()) {
			pop();
			task = peek();
		}
		return task;
	}

	public void add(E newE) {
		Node<E> n = getRoot();
		while (n.next != null) {
			int compare = compare(n.next.element, newE);
			if (compare > 0) {
				break;
			}
			if (compare == 0) {
				// there should only be one other task ever elevated to same
				// priority
				if (n.next.next != null
						&& n.next.next.element.getPriority() == n.next.element
								.getPriority()) {
					throw new IllegalArgumentException("Task of priority "
							+ newE.getPriority() + " already exists ("
							+ n.next.next.element.getName() + ")");

				}
				if (newE.getPriority() != newE.getOriginalPriority()) {
					// new task is elevated to same priority as this task
					break;
				}
			}
			n = n.next;
		}
		n.next = new Node<E>(newE, n.next);
	}

	/**
	 * 
	 * @param task1
	 * @param task2
	 * @return a negative, zero, or positive number if task1 is lower, equal, or
	 *         higher priority than task2
	 * 
	 */
	@Override
	protected int compare(Task task1, Task task2) {
		if (task1.getPriority() < task2.getPriority()) {
			return -1;
		}
		if (task1.getPriority() > task2.getPriority()) {
			return +1;
		}
		return 0;
	}

	public void promote(E owner, int ceiling) throws SchedulerException {
		Log.log(this, "Promoting " + owner + " to priority " + ceiling);
		E e = remove(owner);
		if (e != null) {
			owner.promote(ceiling);
			add(owner);
		} else {
			throw new SchedulerException("Could not find " + owner
					+ " to promote!");
		}
	}

	/**
	 * 
	 * @param owner
	 * @return true if owner is still at a promoted priority, false otherwise
	 */
	public boolean demote(E owner) {
		boolean stillPromoted = owner.demote();
		if (stillPromoted) {
			remove(owner);
			add(owner);
			Log.log(this, "Demoted " + owner);
			return true;
		}
		return false;

	}

}
